﻿using ICSharpCode.SharpZipLib.Zip;
using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;

namespace ProCodes.Utilities
{
    public static class Backups
    {
        static Hashtable ExcludeDirs = new Hashtable();

        /// <summary>
        /// Gets the start folder.
        /// </summary>
        /// <value>The start folder.</value>
        static string StartFolder
        { get { return Properties.IO.Default.MainContainingFolderPath; } }
        /// <summary>
        /// Gets the output zip path.
        /// </summary>
        /// <value>The output zip path.</value>
        static string OutputZipPath
        { get { return Properties.IO.Default.OutputZipPath; } }
        /// <summary>
        /// Gets the zip password.
        /// </summary>
        /// <value>The zip password.</value>
        static string ZipPassword
        { get { return Properties.ApplicationBackups.Default.ZipPassword; } }
        /// <summary>
        /// Gets the excluded dirs.
        /// </summary>
        /// <value>The excluded dirs.</value>
        static string ExcludedDirs
        { get { return Properties.ApplicationBackups.Default.ExcludeDirs; } }

        /// <summary>
        /// Creates the backup.
        /// </summary>
        public static string CreateBackup()
        {
            Guid backupFileName = Guid.NewGuid();
            ExcludeDirs.Clear();
            string[] dirs = Backups.ExcludedDirs.Split('|');
            foreach (string dir in dirs)
                ExcludeDirs.Add(dir.ToUpper(), dir.ToUpper());
            ZipIt(Backups.StartFolder, Backups.OutputZipPath +
                "\\" +
                DateTime.Now.ToString(backupFileName.ToString())
                + ".zip", Backups.ZipPassword);

            return backupFileName.ToString() + ".zip";
        }

        /// <summary>
        /// Zips it.
        /// </summary>
        /// <param name="Path">The path.</param>
        /// <param name="outPathAndZipFile">The out path and zip file.</param>
        /// <param name="password">The password.</param>
        public static void ZipIt(string Path, string outPathAndZipFile, string password)
        {
            string OutPath = outPathAndZipFile;
            ArrayList ar = GenerateFileList(Path); // generate file list
            // find number of chars to remove from orginal file path
            int TrimLength = (Directory.GetParent(Path)).ToString().Length;
            //TrimLength += 1; //remove '\'
            FileStream ostream;
            byte[] obuffer;
            ZipOutputStream oZipStream = new ZipOutputStream(System.IO.File.Create(OutPath)); // create zip stream
            if (password != String.Empty) oZipStream.Password = password;
            oZipStream.SetLevel(9); // 9 = maximum compression level
            ZipEntry oZipEntry;
            foreach (string Fil in ar) // for each file, generate a zipentry
            {
                oZipEntry = new ZipEntry(Fil.Remove(0, TrimLength));
                oZipStream.PutNextEntry(oZipEntry);

                if (!Fil.EndsWith(@"/")) // if a file ends with '/' its a directory
                {
                    ostream = File.OpenRead(Fil);
                    obuffer = new byte[ostream.Length]; // byte buffer
                    ostream.Read(obuffer, 0, obuffer.Length);
                    oZipStream.Write(obuffer, 0, obuffer.Length);
                    Console.Write(".");
                    ostream.Close();
                }
            }
            oZipStream.Finish();
            oZipStream.Close();
        }

        /// <summary>
        /// Generates the file list.
        /// </summary>
        /// <param name="Dir">The dir.</param>
        /// <returns></returns>
        private static ArrayList GenerateFileList(string Dir)
        {
            ArrayList mid = new ArrayList();
            bool Empty = true;
            foreach (string file in Directory.GetFiles(Dir)) // add each file in directory
            {
                mid.Add(file);
                Empty = false;
            }

            if (Empty)
            {
                if (Directory.GetDirectories(Dir).Length == 0) // if directory is completely empty, add it
                {
                    mid.Add(Dir + @"/");
                }
            }
            foreach (string dirs in Directory.GetDirectories(Dir)) // do this recursively
            {
                // set up the excludeDir test
                string testDir = dirs.Substring(dirs.LastIndexOf(@"\") + 1).ToUpper();
                if (Backups.ExcludeDirs.Contains(testDir))
                    continue;
                foreach (object obj in GenerateFileList(dirs))
                {
                    mid.Add(obj);
                }
            }
            return mid; // return file list
        }
    }
}
